package com.spzx.channel.service.impl;

import com.alibaba.fastjson2.JSON;
import com.spzx.channel.domain.ItemVo;
import com.spzx.channel.service.IItemService;
import com.spzx.common.core.constant.SecurityConstants;
import com.spzx.common.core.domain.R;
import com.spzx.common.core.exception.ServiceException;
import com.spzx.product.api.RemoteProductService;
import com.spzx.product.api.domain.Product;
import com.spzx.product.api.domain.ProductDetails;
import com.spzx.product.api.domain.ProductSku;
import com.spzx.product.api.domain.vo.SkuPrice;
import com.spzx.product.api.domain.vo.SkuStockVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;

@Service
@Slf4j
public class ItemServiceImpl implements IItemService {

    @Autowired
    private RemoteProductService remoteProductService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;


    /**
     * 性能优化：
     *      远程调用6个接口，采用同步请求方式，时间较长。需要缩短调用者端响应时间(客户端浏览器等待时间)
     *
     *      这段代码通过 异步编排 和 线程池 将原本串行的6次远程调用优化为并行执行，显著提升了商品详情页的加载速度
     *      需要采用  线程池  + 异步编排(CompletableFuture)  提高性能。
     *
     *
     * @param skuId
     * @return
     * @throws Exception
     */
    @Override
    public ItemVo item(Long skuId) throws Exception {

        //用bitmap判断数据是否存在，存在就调用远程接口去获取：  先查缓存 ， 不存在再查数据库
        String dataKey = "sku:product:data";
        Boolean isExists = redisTemplate.opsForValue().getBit(dataKey, skuId);
        if (!isExists){
            throw new ServiceException("数据不存在");//直接抛异常不用再去缓存穿透
        }

        ItemVo itemVo = new ItemVo();

        //CompletableFuture是Java 8引入的一个强大的异步编程工具，它实现了Future和CompletionStage接口。它提供了丰富的非阻塞式编程方法，支持函数式编程，可以组合多个异步操作，处理它们的完成结果或异常
        //supplyAsync(Runnable runnable, Executor executor): 不需要传入参数，异步执行一个有返回值的任务（Supplier）
        CompletableFuture<ProductSku> productSkuCompletableFuture = CompletableFuture.supplyAsync(() -> {
            //任务1.获取sku信息
            R<ProductSku> productSkuResult = remoteProductService.getProductSku(skuId, SecurityConstants.INNER);
            if (R.FAIL == productSkuResult.getCode()) {
                throw new ServiceException(productSkuResult.getMsg());
            }
            ProductSku productSku = productSkuResult.getData();
            itemVo.setProductSku(productSku);
            return productSku;//supplyAsync: 异步执行一个有返回值的任务，返回给下面调用，下面那个线程拿到结果才执行是串行
        }, threadPoolExecutor);


        //任务2.获取商品信息
        //此时需要productSkuCompletableFuture上一个的返回后的结果
        //thenAcceptAsync消费productSku但不返回结果使用 thenAcceptAsync 。链式调用，确保先获取 ProductSku 再获取 Product
        CompletableFuture<Void> productCompletableFuture = productSkuCompletableFuture.thenAcceptAsync((productSku) -> {
            R<Product> productResult = remoteProductService.getProduct(productSku.getProductId(), SecurityConstants.INNER);
            if (R.FAIL == productResult.getCode()) {
                throw new ServiceException(productResult.getMsg());
            }
            Product product = productResult.getData();
            itemVo.setProduct(product);
            itemVo.setSliderUrlList(Arrays.asList(product.getSliderUrls().split(",")));
            itemVo.setSpecValueList(JSON.parseArray(product.getSpecValue()));
        }, threadPoolExecutor);


        //任务3.获取商品最新价格
        //此时不需要上一个结果返回后在执行，直接用CompletableFuture
        CompletableFuture<Void> skuPriceCompletableFuture = CompletableFuture.runAsync(() -> {
            R<SkuPrice> skuPriceResult = remoteProductService.getSkuPrice(skuId, SecurityConstants.INNER);
            if (R.FAIL == skuPriceResult.getCode()) {
                throw new ServiceException(skuPriceResult.getMsg());
            }
            SkuPrice skuPrice = skuPriceResult.getData();
            itemVo.setSkuPrice(skuPrice);
        }, threadPoolExecutor);


        //任务4.获取商品详情
        //需要返回结果用productSkuCompletableFuture
        CompletableFuture<Void> productDetailsCompletableFuture = productSkuCompletableFuture.thenAcceptAsync((productSku) -> {
            R<ProductDetails> productDetailsResult = remoteProductService.getProductDetails(productSku.getProductId(), SecurityConstants.INNER);
            if (R.FAIL == productDetailsResult.getCode()) {
                throw new ServiceException(productDetailsResult.getMsg());
            }
            ProductDetails productDetails = productDetailsResult.getData();
            itemVo.setDetailsImageUrlList(Arrays.asList(productDetails.getImageUrls().split(",")));
        }, threadPoolExecutor);


        //任务5.获取商品规格对应商品skuId信息
        //需要返回结果用productSkuCompletableFuture
        CompletableFuture<Void> skuSpecValueCompletableFuture = productSkuCompletableFuture.thenAcceptAsync((productSku) -> {
            R<Map<String, Long>> skuSpecValueResult = remoteProductService.getSkuSpecValue(productSku.getProductId(), SecurityConstants.INNER);
            if (R.FAIL == skuSpecValueResult.getCode()) {
                throw new ServiceException(skuSpecValueResult.getMsg());
            }
            Map<String, Long> skuSpecValueMap = skuSpecValueResult.getData();
            itemVo.setSkuSpecValueMap(skuSpecValueMap);
        }, threadPoolExecutor);


        //任务6.获取商品库存信息
        CompletableFuture<Void> skuStockCompletableFuture = productSkuCompletableFuture.thenAcceptAsync((productSku) -> {
            R<SkuStockVo> skuStockResult = remoteProductService.getSkuStock(skuId, SecurityConstants.INNER);
            if (R.FAIL == skuStockResult.getCode()) {
                throw new ServiceException(skuStockResult.getMsg());
            }
            SkuStockVo skuStockVo = skuStockResult.getData();
            itemVo.setSkuStockVo(skuStockVo);
            productSku.setStockNum(skuStockVo.getAvailableNum());
        }, threadPoolExecutor);

        //组合任务。等待6个任务都完成，主线程/主任务再继续。
        CompletableFuture.allOf(productSkuCompletableFuture,
                productCompletableFuture,
                skuPriceCompletableFuture,
                productDetailsCompletableFuture,
                skuSpecValueCompletableFuture,
                skuStockCompletableFuture).join();//join是为了阻塞主线程，必须要封装完再返回数据
                //allOf：组合所有 CompletableFuture，当全部任务完成时触发。
                //join()：阻塞主线程，确保所有异步任务完成后再返回结果。

        return itemVo;
    }
}